package socketmvc.core.packets.basic.impl;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
//import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson2.util.TypeUtils;
import socketmvc.core.exception.CouldNotCastFieldTypeException;
import socketmvc.core.util.TypeReference;
import socketmvc.core.packets.basic.IPacket;
import socketmvc.core.packets.basic.TextBody;
import socketmvc.core.util.ClassUtils;
import socketmvc.core.util.JsonKit;
import socketmvc.core.exception.CouldNotGetFieldException;

import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;

public class JsonTextBody implements TextBody, Serializable {

    // { @h : {@cmd:? , @cbn:? , @iscb: ? } , @b : ? }
    @Serial
    private static final long serialVersionUID = -2655453456889L;

    @JSONField(name = "@h")
    private JSONObject headers = new JSONObject();

    @JSONField(name = "@b")
    private Object body;

    private JSONObject bodyAsJsonObj;


    public void setHeader(String key,Object val){
        if (headers == null){
            headers = new JSONObject();
        }
        headers.put(key,val);
    }

    public JSONObject getHeaders(){
        return headers;
    }

    public void setHeaders(JSONObject header){
        this.headers = header;
    }

    public String getHeader(String key){
        return headers.getString(key);
    }

    @JSONField(serialize = false)
    public String getCommand(){
        return getHeader(IPacket.CMD_KEY);
    }
    public void setCommand(String cmd){
        setHeader(IPacket.CMD_KEY,cmd);
    }

    public void setBody(Object body){
        this.bodyAsJsonObj = null;
        this.body = body;
    }

    public Object getBody(){
        return this.body;
    }

    @JSONField(serialize = false)
    public String getResponseKey(){
        return getHeader(IPacket.RESPONSE_KEY);
    }

    public void setResponseKey(String callName){
        setHeader(IPacket.RESPONSE_KEY,callName);
    }

    @JSONField(serialize = false)
    public boolean isResponse(){
        return "1".equals(getHeader(IPacket.IS_RESPONSE_KEY));
    }

    public void setResponse(boolean isResponse){
        setHeader(IPacket.IS_RESPONSE_KEY,isResponse ? 1 : 0);
    }

    @Override
    public String toString() {
        return JsonKit.toJSONString(this);
    }

    @Override
    public <T> T toJavaObject(Class<T> clazz) {
        Object param = getBody();
        return TypeUtils.cast(param, clazz);
    }

    @Override
    public <T> T toJavaObject(Type type) {
        Object param = getBody();
        return TypeUtils.cast(param,type);
        //return TypeUtils.cast(param, type, ParserConfig.getGlobalInstance());
    }

    @Override
    public <T> T toJavaObject(TypeReference<T> type) {
        Object param = getBody();
        return TypeUtils.cast(param,type.getType());
        //return TypeUtils.cast(param, type, ParserConfig.getGlobalInstance());
    }

    @Override
    public String getString(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getString(key);
    }

    @Override
    public BigDecimal getBigDecimal(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getBigDecimal(key);
    }

    @Override
    public int getIntValue(String key) {
        Object param = getBody();
        if (param == null){
            throw new RuntimeException("null cannot cast int");
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getIntValue(key);
    }

    @Override
    public Integer getInteger(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getInteger(key);
    }

    @Override
    public BigInteger getBigInteger(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getBigInteger(key);
    }

    @Override
    public long getLongValue(String key) {
        Object param = getBody();
        if (param == null){
            throw new RuntimeException("null cannot cast long");
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getLongValue(key);
    }

    @Override
    public Long getLong(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getLong(key);
    }

    @Override
    public double getDoubleValue(String key) {
        Object param = getBody();
        if (param == null){
            throw new RuntimeException("null cannot cast double");
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getDoubleValue(key);
    }

    @Override
    public Double getDouble(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getDouble(key);
    }

    @Override
    public float getFloatValue(String key) {
        Object param = getBody();
        if (param == null){
            return 0;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getFloatValue(key);
    }

    @Override
    public Float getFloat(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getFloat(key);
    }

    @Override
    public short getShortValue(String key) {
        Object param = getBody();
        if (param == null){
            return 0;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getShortValue(key);
    }

    @Override
    public Short getShort(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getShort(key);
    }

    @Override
    public boolean getBooleanValue(String key) {
        Object param = getBody();
        if (param == null){
            return false;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getBooleanValue(key);
    }

    @Override
    public Boolean getBoolean(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getBoolean(key);
    }

    @Override
    public Byte getByte(String key) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getByte(key);
    }

    @Override
    public byte getByteValue(String key) {
        Object param = getBody();
        if (param == null){
            return 0;
        }
        checkParam(key,param);

        return castBodyToJsonObj(param).getByteValue(key);
    }

    @Override
    public <T> T getObject(String key, Class<T> clazz) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);
        Object obj = castBodyToJsonObj(param).get(key);
        try {
//            return TypeUtils.cast(obj, clazz, ParserConfig.getGlobalInstance());
            return TypeUtils.cast(obj, clazz);
        }catch (JSONException e){
            throw new CouldNotCastFieldTypeException(String.format("参数%s 类型转换异常，期望：%s ,实际：%s",key,obj.getClass(),clazz), e);
        }
    }

    @Override
    public <T> T getObject(String key, Type type) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);
        Object obj = castBodyToJsonObj(param).get(key);
        try {
//            return TypeUtils.cast(obj, type, ParserConfig.getGlobalInstance());
            return TypeUtils.cast(obj, type);
        }catch (JSONException e){
            throw new CouldNotCastFieldTypeException(String.format("参数%s 类型转换异常，期望：%s ,实际：%s",key,obj.getClass(),type), e);
        }
    }

    @Override
    public <T> T getObject(String key, TypeReference<T> typeReference) {
        Object param = getBody();
        if (param == null){
            return null;
        }
        checkParam(key,param);
        Object obj = castBodyToJsonObj(param).get(key);
        try {
            //return TypeUtils.cast(obj, typeReference.getType(), ParserConfig.getGlobalInstance());
            return TypeUtils.cast(obj, typeReference.getType());
        }catch (JSONException e){
            throw new CouldNotCastFieldTypeException(String.format("参数%s 类型转换异常，期望：%s ,实际：%s",key,obj.getClass(),typeReference.getType()), e);
        }
    }

    private void checkParam(String key,Object param){
        // 基本数据类型
        if (isPrimitive(param)){
            String methodSign = String.format("%s#get(%s(%s))",param.getClass().getSimpleName(),key,key.getClass().getSimpleName());
            throw new CouldNotGetFieldException("不能从基本数据类型对象中获取%s的值，[param : %s , key : %s , sign : %s]".formatted(key,param,key,methodSign));
        }

        // 集合类型
        if (param instanceof Collection){
            String methodSign = String.format("%s#get(%s(%s))",param.getClass().getSimpleName(),key,key.getClass().getSimpleName());
            throw new CouldNotGetFieldException("不能从集合数据类型对象中获取%s的值，[param : %s , key : %s , sign : %s]".formatted(key,JsonKit.toJSONString(param),key,methodSign));
        }

        // 数组类型
        if (param.getClass().isArray()){
            String methodSign = String.format("%s#get(%s(%s))",param.getClass().getSimpleName(),key,key.getClass().getSimpleName());
            throw new CouldNotGetFieldException("不能从数组数据类型对象中获取%s的值，[param : %s , key : %s , sign : %s]".formatted(key,JsonKit.toJSONString(param),key,methodSign));
        }
    }

    private boolean isPrimitive(Object param){
        Class<?> paramClass = param.getClass();
        return paramClass.isPrimitive() || paramClass.equals(String.class) || ClassUtils.classAssignableFrom(paramClass, Number.class);
    }

    private JSONObject castBodyToJsonObj(Object param){
        if (bodyAsJsonObj == null){
            //bodyAsJsonObj = JSONObject.from(param);
            bodyAsJsonObj = (JSONObject) JSONObject.toJSON(param);
        }
        return bodyAsJsonObj;
    }
}
